package cn.lu.joke.util;

import android.content.Context;
import android.os.Environment;
import android.os.Handler;
import android.os.storage.StorageManager;

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.lang.ref.WeakReference;
import java.lang.reflect.Array;
import java.lang.reflect.Method;
import java.nio.channels.FileChannel;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
import java.util.Queue;
import java.util.Stack;

import cn.lu.joke.App;

public class FileUtils {

    private static Queue<File> queue = new LinkedList<File>();

    static WeakReference<Context> mContext;

    static Handler mHandler;

    static LoadingIndexListener indexListener;

    static String defaultStorage;

    public static String SDCardPath;
    public static boolean hasSDCard;



    static  {
        mContext = new WeakReference<Context>(App.get());
    }

    /**
     * 通过反射调用获取内置存储和外置sd卡根路径(通用)
     * @return
     */
    public static List<String> getStoragePath() {

        StorageManager mStorageManager = (StorageManager) mContext.get().getSystemService(Context.STORAGE_SERVICE);
        Class<?> storageVolumeClazz = null;
        List<String> pathList = new ArrayList<String>();
        try {
            storageVolumeClazz = Class.forName("android.os.storage.StorageVolume");
            Method getVolumeList = mStorageManager.getClass().getMethod("getVolumeList");
            Method getPath = storageVolumeClazz.getMethod("getPath");
            Method isRemovable = storageVolumeClazz.getMethod("isRemovable");
            Object result = getVolumeList.invoke(mStorageManager);
            final int length = Array.getLength(result);
            String phonePath = Environment.getExternalStorageDirectory().getAbsolutePath();
            for (int i = 0; i < length; i++) {
                Object storageVolumeElement = Array.get(result, i);
                String path = (String) getPath.invoke(storageVolumeElement);
                if (!new File(path).exists()) {
                    continue;
                }
                if (phonePath.equals(path)) {
                    pathList.add(0, path);
                } else if (path.toLowerCase().contains("sdcard")) {
                    pathList.add(1, path);
                } else {
                    pathList.add(path);
                }
                //boolean removable = (Boolean) isRemovable.invoke(storageVolumeElement);

            }
            for (String path : pathList) {
                if (path.toLowerCase().contains("sdcard")) {
                    hasSDCard = true;
                    SDCardPath = path;
                    break;
                } else {
                    hasSDCard = false;
                }
            }
            return pathList;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return pathList;
    }

    private static String getPath(File file) {
        for (File f : file.listFiles()) {
            if (f.isDirectory()) {
                queue.offer(f);
                continue;
            }
            if ("index.html".equals(f.getName().toLowerCase())) {
                return f.getAbsolutePath();
            }
        }

        while (!queue.isEmpty()) {
            File file1 = queue.poll();
            if (!file1.canRead()) {
                continue;
            }
            for (File f : file1.listFiles()) {
                if (f.isDirectory()) {
                    queue.offer(f);
                    continue;
                }
                if ("index.html".equals(f.getName().toLowerCase())) {
                    return f.getAbsolutePath();
                }
            }
        }

        return null;
    }

    public static void setIndexListener(LoadingIndexListener indexListener) {
        FileUtils.indexListener = indexListener;
    }

    public interface LoadingIndexListener{
        void loadCompleter(String index);
    }

    //-----------------------------------------------utils------------------------------------------------------//
    private static final int size = 1024 * 512;

    private static Queue<File> dirQueue;
    private static Stack<File> dirStack;

    private static final String ACTION_COPY = "copy";
    private static final String ACTION_CUT = "cut";

    private static long start;
    private static long fileNum;
    private static long dirNum;

    public static void main(String[] args) {
        // nioCopyFile(src.getAbsolutePath(), destFileName, true);
        /*
         * 删除完成, 纯递归--->共有693个文件夹,12468个文件---耗时-->14317
         * 删除完成,广度非递归--->共有693个文件夹,12468个文件---耗时-->14609
         * 删除完成,深度非递归--->共有693个文件夹,12468个文件---耗时-->13676
         */

        File src1 = new File("d:", "bb");
        File fil[] = src1.listFiles();
        for (File file : fil) {
            System.out.println(000);
        }
        File src2 = new File("e:", "b1");// nioTransCopyFile(src1, src2);
        // nioTransCopyFile(src1, src2);
        // NioCopyFile(src1.getAbsolutePath(), src2.getAbsolutePath());
        // copy(src1.getAbsolutePath(), src2.getAbsolutePath());
        // deleteFile(src2);
        System.out.println("copy is complete");

        // FileUtil util = new FileUtil();
        // start = System.currentTimeMillis();
        // util.deleteFile(src2);
        // long end = System.currentTimeMillis();
        // System.out.println("删除完成, 广度非递归--->共有" + util.dirNum + "个文件夹," +
        // util.fileNum + "个文件---耗时-->" + (end - start));

        // start = System.currentTimeMillis();
        // deleteFile(src1);
        // long end3 = System.currentTimeMillis();
        // System.out.println("删除完成, 深度非递归--->共有" + dirNum + "个文件夹," + fileNum +
        // "个文件---耗时-->" + (end3 - start));
        // dirNum = 0;
        // fileNum = 0;

    }

    public static void copy2(String srcPath, String desPath, Handler handler) {
        long totalSize = 0;
        Stack<File> stack = new Stack<File>();
        //stack.push(file);
        File files[] = null;
        while (!stack.isEmpty()) {
            files = stack.pop().listFiles();
            for (File f : files) {
                if (f.isDirectory()) {
                    stack.push(f);
                    continue;
                }
                totalSize += f.length();
            }
        }
    }

    public static boolean copy(String srcPath, String desPath) {
        return NioCopyFile(srcPath, desPath, ACTION_COPY);
    }

	/*@Deprecated
	public static void cut(String srcPath, String desPath) {
		NioCopyFile(srcPath, desPath, ACTION_CUT);
	}

	public static void move(String srcPath, String desPath) {
		NioCopyFile(srcPath, desPath, ACTION_CUT);
	}*/

    public static boolean deleteForAndroid(String path) {
        File file = new File(path);
        if (!file.exists()) {
            return true;
        }
        File refile = new File(path + System.currentTimeMillis());
        file.renameTo(refile);
        return deleteFile(refile);
    }

    public static boolean delete(String path) {
        return deleteFile(new File(path));
    }

    public static void rename(String oldPathName, String newPathName) {

    }

    public static long getTotalSize(String path) {
        File file = new File(path);
        if (!file.exists()) {
            return 0;
        }
        if (file.isFile()) {
            return file.length();
        }
        long totalSize = 0;
        Stack<File> stack = new Stack<File>();
        stack.push(file);
        File files[] = null;
        while (!stack.isEmpty()) {
            files = stack.pop().listFiles();
            for (File f : files) {
                if (f.isDirectory()) {
                    stack.push(f);
                    continue;
                }
                totalSize += f.length();
            }
        }
        return totalSize;
    }

    public static boolean nioCopyFile(File srcFile, File desFile) {
        return nioTransCopyFile(srcFile, desFile);
    }

    //深度遍历复制文件
    public static boolean copyFile(String srcPath, String desPath) {
        File srcFile = new File(srcPath);
        File desFile = new File(desPath);
        if (!srcFile.exists()) {
            return false;
        }
        if (!desFile.exists() || !desFile.isDirectory()) {
            return false;
        }
        desFile = new File(desPath + "/" + srcFile.getName());
        if (srcFile.isDirectory()) {
            desFile.mkdir();
            File files[] = srcFile.listFiles();
            // 实目录
            if (files != null && files.length > 0) {
                if (dirStack == null) {
                    dirStack = new Stack<File>();
                }
                int index = srcPath.length();
                String path = desFile.getAbsolutePath();
                dirStack.push(srcFile);
                while (!dirStack.isEmpty()) {
                    files = dirStack.pop().listFiles();
                    for (File subFile : files) {
                        desFile = new File(path + "/" + subFile.getAbsolutePath().substring(index));
                        if (subFile.isDirectory()) {
                            desFile.mkdir();
                            if (subFile.list().length > 0) {
                                // 实目录,暂时保留
                                dirStack.push(subFile);
                            }
                        } else {
                            // 是文件
                            nioBufferCopy(subFile, desFile);
                        }
                    }
                }
            }
        } else {
            // 是文件
            nioBufferCopy(srcFile, desFile);
        }
        return true;
    }

    private static boolean NioCopyFile(String srcPath, String desPath, String action) {
        boolean isSuccess = false;
        File srcFile = new File(srcPath);
        File desFile = new File(desPath);

        if (!srcFile.exists()) {
            return false;
        }
        if (!desFile.isDirectory()) {
            desFile.mkdirs();
        }
        if (srcFile.isDirectory()) {
            String currentPathParent = null;
            if (!srcPath.equals(desPath)) {
                if (!srcFile.getName().equals(desFile.getName())) {
                    new File(currentPathParent = desPath + File.separatorChar + srcFile.getName()).mkdirs();
                } else {
                    // 目的源和复制源不是同一个文件路径，但目录名相同(/a/b/c)<--->(/a/c)
                    currentPathParent = desPath;
                }
            } else {
                // 目的源和复制源是同一个文件路径(/a/b/c)<--->(/a/b/c)
                int i = 1;
                File file = new File(desPath + "-copy");
                while (!file.mkdir()) {
                    file = new File(desPath + "-" + "copy" + i);
                    i++;
                }
                currentPathParent = file.getAbsolutePath();
                file = null;
            }
            if (srcFile.listFiles().length > 0) {
                // 实目录
                if (dirQueue == null) {
                    dirQueue = new LinkedList<File>();
                }
                dirQueue.offer(srcFile);
                while (!dirQueue.isEmpty()) {
                    for (File subFile : dirQueue.poll().listFiles()) {
                        if (subFile.isDirectory()) {
                            new File(currentPathParent + subFile.getAbsolutePath().substring(srcPath.length())).mkdir();
                            if (subFile.list().length > 0) {
                                // 是实目录
                                dirQueue.offer(subFile);
                            }
                        } else {
                            // 是文件
                            isSuccess = nioTransCopyFile(subFile, new File(
                                    currentPathParent + subFile.getAbsolutePath().substring(srcPath.length())));
                        }
                    }
                }
            }
        } else {
            // 复制源是文件
            // 复制源是文件,目的源是文件夹(/a/b/c.ff)<--->(/a/b)
            if (!desFile.exists()) {
                desFile.mkdirs();
            }
            if (srcPath.equals(desPath + File.separator + srcFile.getName())) {
                // 目的源和复制源是同一个文件路径的文件(/a/b/c.ff)<--->(/a/b/c.ff)
                int i = 1;
                File file = new File(desPath + File.separator + "copy-" + srcFile.getName());
                while (file.exists()) {
                    file = new File(desPath + File.separator + "copy" + i + "-" + srcFile.getName());
                    i++;
                }
                desFile = file;
                file = null;
            } else {
                // 注： 如果出现目的源和复制源不是同一个文件路径的文件，但文件名相同(/a/b/c.ff)<--->(/a/c.ff)
                // 则会进行覆盖已存在的文件
                desFile = new File(desPath + File.separator + srcFile.getName());
            }
            /*
             * else { // 复制源是文件,目的源是文件 if (srcPath.equals(desPath)) { //
             * 目的源和复制源是同一个文件路径的文件(/a/b/c.ff)<--->(/a/b/c.ff) int i = 1; File
             * file = new File(desFile.getParent() + File.separator + "copy-" +
             * desFile.getName()); while (file.exists()) { file = new
             * File(desFile.getParent() + File.separator + "copy" + i + "-" +
             * desFile.getName()); i++; } desFile = file; file = null; } }
             */
            isSuccess = nioTransCopyFile(srcFile, desFile);
        }
        if (ACTION_CUT.equals(action)) {
            if (isSuccess) {
                isSuccess = deleteFile(srcFile);
            }
        }
        return isSuccess;
    }

    /**
     * 如果是目录，则深度遍历删除，否则直接删除
     *
     * @param file
     */
    private static synchronized boolean deleteFile(File file) {
        if (file.isDirectory()) {
            dirNum++;
            if (file.listFiles().length > 0) {
                // 实目录
                if (dirStack == null) {
                    dirStack = new Stack<File>();
                }
                dirStack.push(file);
                while (!dirStack.isEmpty()) {
                    for (File subFile : dirStack.peek().listFiles()) {
                        if (subFile.isDirectory()) {
                            dirNum++;
                            if (subFile.list().length > 0) {
                                // 实目录,暂时保留
                                dirStack.push(subFile);
                            } else {
                                // 空目录,删掉
                                subFile.delete();
                            }
                        } else {
                            fileNum++;
                            // 不是目录,删掉
                            subFile.delete();
                        }
                    }
                    if (dirStack.peek().list().length <= 0) {
                        dirStack.pop().delete();
                    }
                }
            } else {
                // 空目录,删掉
                file.delete();
            }
            if (!file.exists()) {
                return true;
            }
            return false;
        } else {
            fileNum++;
            // 不是目录,删掉
            return file.delete();
        }
    }


    public static long currentSize;
    /**
     * 通过文件通道进行文件复制
     *
     * @param source
     *            源文件
     * @param target
     *            目标文件
     * @return
     */
    private static boolean nioTransCopyFile(File source, File target) {
        if (!source.exists() || !source.isFile()) {
            return false;
        }
        FileChannel iChannel = null;
        FileChannel oChannel = null;
        FileInputStream inStream = null;
        FileOutputStream outStream = null;
        try {
            //currentSize += source.length();
            inStream = new FileInputStream(source);
            outStream = new FileOutputStream(target);

            iChannel = inStream.getChannel();
            oChannel = outStream.getChannel();

            iChannel.transferTo(0, iChannel.size(), oChannel);

            return true;
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            close(inStream, outStream, iChannel, oChannel);
            source = null;
            target = null;
        }
        return false;
    }

    /**
     * 带进度的文件通道复制，(会影响复制速度)
     *
     * @param source
     * @param target
     */
    private static void nioBufferCopy(File source, File target) {
        if (!source.exists() || !source.isFile()) {
            return;
        }
        FileInputStream fis = null;
        FileOutputStream fos = null;
        BufferedInputStream bis = null;
        BufferedOutputStream bos = null;
        try {
            fis = new FileInputStream(source);
            fos = new FileOutputStream(target);
            bis = new BufferedInputStream(fis);
            bos = new BufferedOutputStream(fos);
            int len = 0;
            byte buf[] = new byte[1024];
            while ((len = bis.read(buf)) != -1) {
                currentSize += len;
                bos.write(buf, 0, len);
            }
            bos.flush();
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                if (bos != null) {
                    bos.close();
                }
                if (bis != null) {
                    bis.close();
                }
                if (fos != null) {
                    fos.close();
                }
                if (fis != null) {
                    fis.close();
                }
            } catch (Exception e2) {
            } finally {
                fis = null;
                fos = null;
                bos = null;
                bis = null;
            }
        }
    }

    /**
     * 关闭资源
     *
     * @param in
     *            inputStream
     * @param out
     *            outputStream
     * @param iChannel
     *            inputStreamChannel
     * @param oChannel
     *            outputStreamChannel
     */
    private static void close(InputStream in, OutputStream out, FileChannel iChannel, FileChannel oChannel) {
        try {
            if (oChannel != null) {
                oChannel.close();
                oChannel = null;
            }
            if (iChannel != null) {
                iChannel.close();
                iChannel = null;
            }
            if (in != null) {
                in.close();
                in = null;
            }
            if (out != null) {
                out.close();
                out = null;
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

}
